package com.springboot.orderno.service.impl;

import com.springboot.orderno.common.ResultCode;
import com.springboot.orderno.common.ResultVO;
import com.springboot.orderno.dao.OrderNoDao;
import com.springboot.orderno.model.OrderNo;
import com.springboot.orderno.service.OrderNoService;
import com.springboot.orderno.service.RedisService;
import com.springboot.orderno.util.DateTimeUtil;
import com.springboot.orderno.util.OrderNoEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class OrderNoServiceImpl implements OrderNoService {
    Logger logger = LoggerFactory.getLogger(getClass());

    private static final DateTimeFormatter DF_FMT_PREFIX = DateTimeFormatter.ofPattern("yyMMddHHmmss");
    private static final String KEY = "ORDER_NO";
    private static ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai");

    @Autowired
    private OrderNoDao orderNoDao;

    @Autowired
    private RedisService redisService;

    /**
     * 数据入库示例
     *
     * @param source
     * @param orderNoPrefix
     * @param orderNoType
     * @param orderNoLength
     * @return
     */
    public ResultVO<String> insert(Integer source, String orderNoPrefix, String orderNoType, Integer orderNoLength) {
        ResultVO<String> resultVO = new ResultVO<>();
        OrderNo orderNoParam = new OrderNo();
        if (Objects.isNull(source)) {
            resultVO.resultFail("source must not be null", ResultCode.fail.getCode());
            return resultVO;
        }
        String orderNo = getOrderNO(orderNoPrefix, orderNoType, orderNoLength).getData();
        orderNoParam.put("orderNo", orderNo);
        orderNoParam.put("source", source);
        try {
            long insertNo = orderNoDao.insert(orderNoParam);
            if (insertNo == 0) {
                resultVO.resultFail("数据插入异常，已插入数据条数为0", ResultCode.fail.getCode());
                return resultVO;
            }
        } catch (Exception e) {
            logger.error("数据插入异常：{}", e.getMessage());
            resultVO.resultFail("数据插入异常！", ResultCode.fail.getCode());
            return resultVO;
        }

        resultVO.resultSuccess(orderNo);
        return resultVO;
    }

    /**
     * 订单号生成方法
     *
     * example:四位单号 HDD210118093559,0001,单号后缀由orderNoLength决定
     *
     * @param orderNoPrefix 订单号前缀
     * @param orderNoType   Redis中orderNo类型 示例：PO(payment order), DO(delivery order),完整redis key示例：PO:ORDER_NO_6,
     * 用以区分不同类型的业务redis Key
     * @param orderNoLength 支持4位，6位，8位
     * @return
     */
    public ResultVO<String> getOrderNO(String orderNoPrefix, String orderNoType, Integer orderNoLength) {
        ResultVO<String> resultVO = new ResultVO<>();

        boolean paramValidRes = Objects.isNull(orderNoPrefix) || Objects.isNull(orderNoType) || Objects.isNull(orderNoLength);
        StringBuilder paramValidFailedInfo = new StringBuilder("参数校验失败：");
        if (paramValidRes) {
            if (Objects.isNull(orderNoPrefix)) {
                paramValidFailedInfo.append("orderNoPrefix must not be null; ");
            }
            if (Objects.isNull(orderNoType)) {
                paramValidFailedInfo.append("orderNoType must not be null; ");
            }
            if (Objects.isNull(orderNoLength)) {
                paramValidFailedInfo.append("orderNoLength must not be null; ");
            }
            logger.error("{}", paramValidFailedInfo);
            resultVO.resultFail(paramValidFailedInfo.toString(), ResultCode.fail.getCode());
            return resultVO;
        }

        LocalDateTime dataTime = LocalDateTime.now(ZONE_ID);
        String keyTotalName = orderNoType.concat(":").concat(KEY).concat("_").concat(String.valueOf(orderNoLength));
        boolean existsKey = redisService.existsKey(keyTotalName);
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date expireDate = (Date) dateFormat.parseObject(DateTimeUtil.getTomorrowStartTime());
            if (!existsKey) {
                redisService.set(keyTotalName, 0);
                redisService.expireKeyAt(keyTotalName, expireDate);
            }
            long orderVal = redisService.incrementAndGet(keyTotalName);
            if (orderVal >= getMaxNoSuffix(orderNoLength)) {
                redisService.set(keyTotalName, 0);
                redisService.expireKeyAt(keyTotalName, expireDate);
            }

            String suffixFormatReg = "%0".concat(String.valueOf(orderNoLength)).concat("d");
            String oderNo = orderNoPrefix + dataTime.format(DF_FMT_PREFIX) + String.format(suffixFormatReg, orderVal);

            logger.info("key is: {}", oderNo);
            resultVO.resultSuccess(oderNo);
        } catch (ParseException e) {
            logger.error("时间转换错误：{}", e.getMessage());
        } finally {
            lock.unlock();
        }
        return resultVO;
    }

    /**
     * 获取编号最大值
     *
     * @param orderNoLength
     * @return
     */
    private static int getMaxNoSuffix(int orderNoLength) {
        switch (orderNoLength) {
            case 6:
                return OrderNoEnum.LENGTH_SIX.getMaxNo();
            case 8:
                return OrderNoEnum.LENGTH_EIGHT.getMaxNo();
            default:
                return OrderNoEnum.LENGTH_FOUR.getMaxNo();
        }
    }
}